

// 日志器模块
// 分为同步日志器和异步日志器
// 包含格式化模块对象，日志输出的等级限制 ，日志器名称
// 日志落地数组(由于一个数组可能会有多个落地方向)——这部分称为落地方式
// 互斥锁保证线程安全

#ifndef __M_LOGGER_H__
#define __M_LOGGER_H__

#include "logFormat.hpp"
#include "util.hpp"
#include "logSink.hpp"
#include "lopper.hpp"

#include <atomic>
#include <mutex>
#include <memory>
#include <stdarg.h>
#include <sstream>

namespace xglog
{
    // 基类日志器
    class logger
    {
    public:
        using ptr = std::shared_ptr<logger>;
        logger(const std::string &logger_name,
               LogLevel::value level,
               Formatter::ptr formatter,
               std::vector<SinkPtr> sinks)
            : _logger_name(logger_name),
              _limit_level(level),
              _formatter(formatter),
              _sinks(sinks.begin(), sinks.end())
        {
        }
        const std::string& getName()
        {
            return _logger_name;
        }
        // 不同的等级，对日志进行格式化并且完成日志的落地工作
        void debug(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            // 1,如果当前等级小于最低的日志限制，直接返回
            if (_limit_level > LogLevel::value::DEBUG)
                return;
            // 2,对fmt格式化字符和不定参进行字符串组织,得到日志消息
            va_list ap;
            va_start(ap, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret == -1)
            {
                std::cout << "vasprintf failed!!\n";
                return;
            }
            va_end(ap);
            // 3,构建logMsg对象
            logMessage logMsg(LogLevel::value::DEBUG, line, file, _logger_name, res);
            // 4,通过格式化方法对logMsg格式化
            std::stringstream ss;
            _formatter->Format(ss, logMsg);
            // 5,完成落地
            log(ss.str().c_str(), ss.str().size());
            free(res);
        }
        void info(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            // 1,如果当前等级小于最低的日志限制，直接返回
            if (_limit_level > LogLevel::value::INFO)
                return;
            // 2,对fmt格式化字符和不定参进行字符串组织,得到日志消息
            va_list ap;
            va_start(ap, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret == -1)
            {
                std::cout << "vasprintf failed!!\n";
                return;
            }
            va_end(ap);
            // 3,构建logMsg对象
            logMessage logMsg(LogLevel::value::INFO, line, file, _logger_name, res);
            // 4,通过格式化方法对logMsg格式化
            std::stringstream ss;
            _formatter->Format(ss, logMsg);
            // 5,完成落地
            log(ss.str().c_str(), ss.str().size());
            free(res);
        }
        void warn(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            // 1,如果当前等级小于最低的日志限制，直接返回
            if (_limit_level > LogLevel::value::WARN)
                return;
            // 2,对fmt格式化字符和不定参进行字符串组织,得到日志消息
            va_list ap;
            va_start(ap, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret == -1)
            {
                std::cout << "vasprintf failed!!\n";
                return;
            }
            va_end(ap);
            // 3,构建logMsg对象
            logMessage logMsg(LogLevel::value::WARN, line, file, _logger_name, res);
            // 4,通过格式化方法对logMsg格式化
            std::stringstream ss;
            _formatter->Format(ss, logMsg);
            // 5,完成落地
            log(ss.str().c_str(), ss.str().size());
            free(res);
        }
        void error(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            // 1,如果当前等级小于最低的日志限制，直接返回
            if (_limit_level > LogLevel::value::ERROR)
                return;
            // 2,对fmt格式化字符和不定参进行字符串组织,得到日志消息
            va_list ap;
            va_start(ap, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret == -1)
            {
                std::cout << "vasprintf failed!!\n";
                return;
            }
            va_end(ap);
            // 3,构建logMsg对象
            logMessage logMsg(LogLevel::value::ERROR, line, file, _logger_name, res);
            // 4,通过格式化方法对logMsg格式化
            std::stringstream ss;
            _formatter->Format(ss, logMsg);
            // 5,完成落地
            log(ss.str().c_str(), ss.str().size());
            free(res);
        }
        void fatal(const std::string &file, size_t line, const std::string &fmt, ...)
        {
            // 1,如果当前等级小于最低的日志限制，直接返回
            if (_limit_level > LogLevel::value::FATAL)
                return;
            // 2,对fmt格式化字符和不定参进行字符串组织,得到日志消息
            va_list ap;
            va_start(ap, fmt);
            char *res;
            int ret = vasprintf(&res, fmt.c_str(), ap);
            if (ret == -1)
            {
                std::cout << "vasprintf failed!!\n";
                return;
            }
            va_end(ap);
            // 3,构建logMsg对象
            logMessage logMsg(LogLevel::value::FATAL, line, file, _logger_name, res);
            // 4,通过格式化方法对logMsg格式化
            std::stringstream ss;
            _formatter->Format(ss, logMsg);
            // 5,完成落地
            log(ss.str().c_str(), ss.str().size());
            free(res);
        }

    protected:
        // 完成日志的落地工作
        virtual void log(const char *data, size_t len) = 0;

    protected:
        std::atomic<LogLevel::value> _limit_level; // 该日志器的输出等级限制
        std::string _logger_name;                  // 日志器名称，用来表示日志器的唯一性
        Formatter::ptr _formatter;                 // 日志的输出格式
        std::vector<SinkPtr> _sinks;               // 日志落地数组(可能会有多个落地方向)
        std::mutex _mtx;
    };

    // 同步日志器
    class Synclogger : public logger
    {
    public:
        Synclogger(const std::string &logger_name,
                   LogLevel::value level,
                   Formatter::ptr formatter,
                   std::vector<SinkPtr> sinks)
            : logger(logger_name, level, formatter, sinks)
        {
        }
        void log(const char *data, size_t len)
        {
            std::unique_lock<std::mutex> _lock(_mtx);
            if (_sinks.empty())
                return;
            for (auto &sink : _sinks)
            {
                sink->log(data, len);
            }
        }
    };

    // 异步日志器
    class Asynclogger : public logger
    {
    public:
        Asynclogger(const std::string &logger_name,
                    LogLevel::value level,
                    Formatter::ptr formatter,
                    std::vector<SinkPtr> sinks,
                    AsyncType type = AsyncType::ASYNC_UNSAFE)
            : logger(logger_name, level, formatter, sinks),
              _lopper(std::make_shared<AsyncWorker>(std::bind(&xglog::Asynclogger::realcallback, this, std::placeholders::_1), type))
        {
        }

        // 完成日志落地的函数
        void log(const char *data, size_t len)
        {
            _lopper->push(data, len);
        }

    private:
        // 实际真正完成日志落地的函数,异步工作器中的回调函数
        void realcallback(Buffer &buf)
        {
            if (_sinks.empty())
            {
                return;
            }
            for (auto &sink : _sinks)
            {
                sink->log(buf.begin(), buf.readAbleSize());
            }
        }

    private:
        AsyncWorker::ptr _lopper; // 异步日志工作器
    };

    // 在实例化出一个日志的过程中，需要传递的参数过多
    // 所以使用建造者模式，分步来构造
    // 通过建造者模式完成日志器的构建
    // 构建局部日志器 或者是全局日志器
    // 同时日志器本身也分为同步日志器和异步日志器
    enum class Logger_Type
    {
        LOG_SYNC,
        LOG_ASYNC,
    };
    // 建造者基类提供一些公共的方法
    // 构建日志器的名称，构建日志器的类型，构建日志器的落地数组，构建日志输出等级，构建日志格式化器
    // 子类继承通过多态的方式实现局部日志器和全局日志器

    // 建造者模式基类
    class LoggerBuilder
    {
    public:
        LoggerBuilder()
            : _logger_type(Logger_Type::LOG_SYNC) // 默认是同步日志器
              ,
              _lopper_type(AsyncType::ASYNC_SAFE) // 默认是安全的
              ,
              _limit_level(LogLevel::value::DEBUG)
        {
        }
        // 构建日志器的名称
        void buildLoggerName(const std::string &logger_name)
        {
            _logger_name = logger_name;
        }
        // 构建日志器类型(同步or异步)
        void buildLoggerType(Logger_Type type)
        {
            _logger_type = type;
        }
        // 构建日志器的输出等级
        void buildLoggerLevel(LogLevel::value level)
        {
            _limit_level = level;
        }
        // 构建日志格式化器
        void buildLoggerFormat(const std::string &pattern)
        {
            _formatter = std::make_shared<Formatter>(pattern);
        }
        // 构建日志器落地方向数组
        template <typename SinkType, typename... Args>
        void buildLoggerSink(Args &&...args)
        {
            SinkPtr sink = SinkFactory::createSink<SinkType>(std::forward<Args>(args)...);
            _sinks.push_back(sink);
        }
        void buildAsyncUnsafe()
        {
            _lopper_type = AsyncType::ASYNC_UNSAFE;
        }
        // 构建对应的日志器(同步or异步)
        virtual logger::ptr build() = 0;

    protected:
        AsyncType _lopper_type;       // 对于异步日志器而言(安全or非安全)
        std::string _logger_name;     // 日志器名称
        Logger_Type _logger_type;     // 日志器类型(同步or异步)
        LogLevel::value _limit_level; // 日志输出等级限制
        Formatter::ptr _formatter;    // 日志格式化器
        std::vector<SinkPtr> _sinks;  // 日志落地方向(可能有多个落地方向)
    };

    // 局部日志器
    class LocalLoggerBuilder : public LoggerBuilder
    {
    public:
        logger::ptr build()
        {
            // 日志器必须有对应的名字
            assert(_logger_name.empty() == false);
            // 如果没有构建格式化器,使用默认构造
            if (_formatter.get() == nullptr)
            {
                _formatter = std::make_shared<Formatter>();
            }
            // 如果没有构建日志格式化器，默认为向标准输出打印日志
            if (_sinks.empty())
            {
                _sinks.push_back(SinkFactory::createSink<StdoutSink>());
            }

            // 构建异步日志器
            if (_logger_type == Logger_Type::LOG_ASYNC)
            {
                return std::make_shared<Asynclogger>(_logger_name,
                                                     _limit_level, _formatter, _sinks, _lopper_type);
            }

            // 同步日志器
            return std::make_shared<Synclogger>(_logger_name,
                                                _limit_level, _formatter, _sinks);
        }
    };

    // 日志器管理器
    // 可以对创建好的日志器进行管理
    // 同时为用户提供一个默认的日志器
    class LoggerManager
    {
    private:
        LoggerManager()
        {
            // 完成默认日志器_root_logger的初始化
             std::unique_ptr<xglog::LoggerBuilder> builder(new xglog::LocalLoggerBuilder());
             builder->buildLoggerName("root");
             _root_logger=builder->build();

             _loggers.insert(std::make_pair("root",_root_logger));
        }

    public:
        static LoggerManager &GetInstance()
        {
            // 自c++11起,在多线程环境下创建静态成员是线程安全的
            static LoggerManager _eto;
            return _eto;
        }
        // 获取一个日志器
        logger::ptr getLogger(const std::string &name)
        {
            std::unique_lock<std::mutex> lock(_mtx);
            auto it=_loggers.find(name);

            if(it==_loggers.end())
            return logger::ptr();

            return it->second;
        }
        //获取默认日志器
        logger::ptr getRoot()
        {
            return  _root_logger;
        }
        // 添加一个日志器
        void addLogger(logger::ptr& logger)
        {
            if(hasLogger(logger->getName()))
            return;
            
            std::unique_lock<std::mutex> lock(_mtx);
            _loggers.insert(std::make_pair(logger->getName(),logger));
        }
        // 判断一个日志器是否存在
        bool hasLogger(const std::string &name)
        {
            std::unique_lock<std::mutex> lock(_mtx);
            auto it = _loggers.find(name);
            if (it == _loggers.end())
                return false;
            return true;
        }

    private:
        std::mutex _mtx;
        logger::ptr _root_logger;                              // 默认日志器
        std::unordered_map<std::string, logger::ptr> _loggers; // 通过哈希表管理日志器,日志器名称为唯一标识
    };
    //使用日志管理器，用户就可以在任意位置使用日志器了
    //至此用户在获取日志器时需要获取单例对象，将日器加入到管理器中，然后调用接口获取对应的日志器
    //这样对于用户来说操作难度变大了
    //为了降低用户的操作成本，需要一个全局的日志器建造者
    //这个建造者会直接将创建好的日志器添加到日志器管理器中，不需要用户操作
    //用户想要使用，可以直接通过日志管理器来获取
    //这样以来，用户创建好的日志器在任何地方都可以使用了

    // 全局日志器
    //作用：可以使用户创建的日志器在任何位置都可以使用
    class GlobalLoggerBuilder : public LoggerBuilder
    {
    public:
        logger::ptr build()
        {
            // 日志器必须有对应的名字
            assert(_logger_name.empty() == false);
            // 如果没有构建格式化器,使用默认构造
            if (_formatter.get() == nullptr)
            {
                _formatter = std::make_shared<Formatter>();
            }
            // 如果没有构建日志格式化器，默认为向标准输出打印日志
            if (_sinks.empty())
            {
                _sinks.push_back(SinkFactory::createSink<StdoutSink>());
            }

            logger::ptr logger;
            // 构建异步日志器
            if (_logger_type == Logger_Type::LOG_ASYNC)
            {
                logger=std::make_shared<Asynclogger>(_logger_name,
                                                     _limit_level, _formatter, _sinks, _lopper_type);
            }
            // 同步日志器
            logger=std::make_shared<Synclogger>(_logger_name,
                                                _limit_level, _formatter, _sinks);

            //将创建好的日志器添加到日志器管理器中
            LoggerManager::GetInstance().addLogger(logger);
            return logger;
        }
    };

}

#endif